use std::fmt;
use std::any::{Any};
use std::rc::{self};
use std::cell::{RefCell};
use weak_table::{PtrWeakKeyHashMap as WeakMap};
use tree;
use geom;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Events, callbacks and dispatchers

pub trait EvtOf<A> {
  fn bind_to(self, disp: &mut EvtDispatcher, cbk: Callback<A>);
}

#[derive(Debug, Clone)]
pub struct EvtId(rc::Rc<()>);

pub struct Callback<A> {
  path: tree::PathBuf,
  func: rc::Rc<dyn Fn(&tree::Path, &dyn Any, A)>,
}

pub type EvtDispatcherPtr = rc::Rc<RefCell<EvtDispatcher>>;

pub struct EvtDispatcher {
  unit_disp: CbDispatcher<()>,
  pos_disp: CbDispatcher<geom::Pos>,
}

struct CbDispatcher<A> {
  map: WeakMap<rc::Weak<()>, Vec<Callback<A>>>,
}

pub enum FiredEvt {
  Notify(EvtId),
  Mouse(EvtId, geom::Pos),
  // TODO: more types
  // e.g. Mouse { id: EvtId, pos: geom::Pos },
}


// Dumb impls

impl<A> Clone for Callback<A> {
  fn clone(&self) -> Self {
    Callback { path: self.path.clone(), func: rc::Rc::clone(&self.func) }
  }
}

impl<A> fmt::Debug for Callback<A> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Callback {{ ... }}")
  }
}

impl<A> PartialEq for Callback<A> {
  fn eq(&self, _: &Self) -> bool { true }
}

impl<A> Eq for Callback<A> {}

impl PartialEq for EvtId {
  fn eq(&self, rhs: &Self) -> bool { rc::Rc::ptr_eq(&self.0, &rhs.0) }
}

impl Eq for EvtId {}


// Constructors

impl EvtId {
  pub fn new() -> Self {
    EvtId(rc::Rc::new(()))
  }
}

impl<A> Callback<A> {
  pub fn new<F>(path: tree::PathBuf, func: F) -> Self
  where F: Fn(&tree::Path, &Any, A) + 'static {
    Callback { path, func: rc::Rc::from(func) }
  }
}

impl EvtDispatcher {
  pub fn new() -> Self {
    EvtDispatcher {
      unit_disp: CbDispatcher::new(),
      pos_disp: CbDispatcher::new(),
    }
  }

  pub fn into_rc_ptr(self) -> EvtDispatcherPtr {
    rc::Rc::new(RefCell::from(self))
  }
}


// Registering callbacks

impl<A> CbDispatcher<A> {
  fn new() -> Self {
    CbDispatcher { map: WeakMap::new() }
  }

  fn register(&mut self, id: EvtId, cbk: Callback<A>) {
    self.map.entry(id.0)
            .or_insert_with(Vec::new)
            .push(cbk)
  }

  fn callbacks(&self, id: EvtId) -> impl Iterator<Item=Callback<A>> {
    self.map
        .get(&id.0)
        .cloned()     // deep copy all callbacks to prevent the
        .into_iter()  // EvtDispatcher from being borrowed for a long time
        .flatten()
  }
}


// Dispatching to callbacks

impl<A> Callback<A> where A: fmt::Debug {
  fn call<P>(self,
             tree: &tree::Live<P, Box<Any>>,
             arg: A) {
    match tree.follow_abstract(&self.path) {
      Err(e) =>
        print!("WARN: could not find node in callback: {:?}\n", e),

      Ok(abs) => {
        print!("execute callback: {:p}, arg={:?}\n", self.func, arg);
        (*self.func)(&self.path, abs.state().as_ref(), arg)
      }
    }
  }
}

impl FiredEvt {
  pub fn dispatch<P>(self,
                     tree: &tree::Live<P, Box<dyn Any>>,
                     evt_disp: &EvtDispatcherPtr) {
    match self {
      FiredEvt::Notify(id) => {
        let cbs = evt_disp.borrow().unit_disp.callbacks(id);
        cbs.for_each(|cb| cb.call(tree, ()))
      }

      FiredEvt::Mouse(id, pos) => {
        let cbs = evt_disp.borrow().pos_disp.callbacks(id);
        cbs.for_each(|cb| cb.call(tree, pos))
      }
    }
  }
}


// EvtOf implementors

// `Some(e)` is equivalent to `e`
// `None` is equivalent to `never_evt()`
impl<E,A> EvtOf<A> for Option<E> where E: EvtOf<A> {
  fn bind_to(self, disp: &mut EvtDispatcher, cbk: Callback<A>) {
    self.map(move |x| x.bind_to(disp, cbk));
  }
}

#[derive(Clone)]
struct NeverEvt;
impl<A> EvtOf<A> for NeverEvt {
  fn bind_to(self, _: &mut EvtDispatcher, _: Callback<A>) {}
}

struct SomeEvt(EvtId);
impl EvtOf<()> for SomeEvt {
  fn bind_to(self, disp: &mut EvtDispatcher, cbk: Callback<()>) {
    disp.unit_disp.register(self.0, cbk)
  }
}

impl EvtOf<geom::Pos> for SomeEvt {
  fn bind_to(self, disp: &mut EvtDispatcher, cbk: Callback<geom::Pos>) {
    disp.pos_disp.register(self.0, cbk)
  }
}

pub fn never_evt<A>() -> impl EvtOf<A> + Clone { NeverEvt }
pub fn unit_evt(id: EvtId) -> impl EvtOf<()> { SomeEvt(id) }
pub fn pos_evt(id: EvtId) -> impl EvtOf<geom::Pos> { SomeEvt(id) }
